Suggestion (#7): ck-notation.js — additive CKN compiler over the shipped verb surface#8
Suggestion (#7): ck-notation.js — additive CKN compiler over the shipped verb surface#8styk-tv wants to merge 1 commit into
Conversation
…r the shipped verb surface (#7) Proposed reference implementation in the direction agreed on #7. cklib is authoritative on the boundary/API — adapt, rewrite, or reject freely. - ck-notation.js: compile(source)→plan (pure, inspectable [{verb,payload}]) + assemble(handle, planOrSource) over ConceptKernel.do(); instance plane only (χ/ρ→create, edges→link, τ→transition, π→verify/provenance); genome plane deferred (typed NotationError). Plan-emitter only — no RDF/quad/query surface; ck.js untouched; zero deps; injects no type-specific field. - tests/smoke-notation.mjs: pure compile goldens + assemble over a mock transport (CK.activate), refs + honest degrade. 27/27; full suite green. - package.json: ./notation export + files + check/test wired. - CHANGELOG: Unreleased; its own cut, not folded into the scoring byte-set.
styk-tv
left a comment
There was a problem hiding this comment.
Review — direction and module shape accepted; it holds the #7 bar. Two wire fixes + one design call before cklib owns it as its own cut. Strong, precisely-scoped contribution — thank you.
I verified rather than took on faith:
✅ Holds the acceptance bar (checked, not assumed)
ck.jsunchanged — confirmed by diff; the three core modules are byte-identical. Module uses onlyhandle.do(verb, payload, opts).- Plan-emitter, not a graph —
compile()returns[{verb, payload, ref?}]; no RDF/quad/query emitted. The AST is a parse tree of the notation, never a graph of kernel data. - Zero authority —
compile()has no imports and no I/O (pure).{$ref}threads the sealed id an earlier step returned into a later payload — what an app does by hand (const t = await create(); link(t.id, …)), not evaluation of kernel data. Every effect goes through the governed door. - Genome plane deferred honestly — a
⟦genome⟧header or σ/α/γ strand throws a typedNotationError('genome_plane_deferred'), not a pretend success. - Honest degrade in
assemble— substrate rejects recorded per-step (never thrown, never fabricated), dependents abort viaskipped_dependency, aggregateokis truthful. - Inspect-then-run split (
compile → plan,assemble → run) is exactly right for the untrusted-JWT posture. Suite green (notation 27/27; full 98/98), real-behavior tests overCK.activate+ mock, in the house style.
🔧 Two substantive fixes before it lands
1. instance.update payload shape — would fail against live pgCK. The URN-ρ path emits a flat { id, …props }. But ck.js's own contract wraps properties under patch — update(id, patch) dispatches { id, patch }, and the header notes instance.update {id, patch:{…}} → update_typed … undeclared keys rejected. So assemble's do('instance.update', { id, holds:'x' }) sends holds as an undeclared top-level key → reject. Emit { id, patch: { …props } }. This path is untested (0 instance.update in the suite) — please add a URN-ρ case to lock the wire shape. (create is correctly flat — create_typed takes {type, …fields} — so only update needs this.)
2. Domain-type IRI resolution hardcodes v3.8 core. resolveType sends a bare name to …/ontology/v3.8/core#<Name>. Two issues: (a) it pins v3.8 while we are CKP v3.9, and those v3.8 ontology IRIs currently 404; (b) a kernel's domain types are its own declared class IRIs (urn:ckp:<project>/type/<Name>), not the protocol core-vocabulary namespace — so bare-name→core emits a type no declared shape matches (the mock accepts it; real create_typed will not). Suggest: keep the core namespace for protocol vocab only, and for domain types require a full IRI/CURIE or make the base injectable (compile(source, { typeBase })); do not pin a version that 404s.
🎛 One design call (you flagged it) — do vs named methods
Running each step through handle.do(verb, payload) keeps the plan a uniform, inspectable [{verb,payload}] — I want to keep it. But it makes the compiler responsible for each verb's exact wire payload, which is precisely how the update nesting slipped (the named update() would have wrapped it for free). So if we keep do (my preference), the rule becomes: every verb's payload is contract-tested against ck.js's shapes — turning the choice into a test obligation, not a latent trap.
🧹 Minor
- Fixture naming: the shipped test uses a specific consumer's kernel name — please use a generic fixture (
Demo.Session/Board, matching thedemo/generic convention in the other smoke tests). Keeps public fixtures neutral. ;-comment stripping runs before quote parsing, so a;inside a quoted value is truncated — edge case; guard or note.τemits noevidence; fine for slice 1, buttransition(id, to, evidence)takes it — a future arg.verified: reply.verified ?? undefinedwrites an explicitundefinedkey — cosmetic.
Disposition
Accepting the direction and the shape — right design, respects the invariants. Not merge-as-is only for the two wire fixes (update patch-nesting + type resolution) and the fixture rename; land those with a URN-ρ test and it rides as its own cut after v1.5.4, exactly as scoped on #7. Thanks for handing it over so cleanly for cklib to own.
Addresses #7. This is a suggestion in the direction agreed there — not a claim on the boundary. cklib is authoritative: adapt the API, rename, restructure, or reject any of it freely. It's offered as a working starting point so the discussion has concrete code to react to, and it's deliberately shaped to the maintainer's answers on #7.
What it is
An additive, separately-importable module —
ck-notation.jsat@conceptkernel/cklib/notation— that turns an instance-plane CKN expression into a construct on a live kernel, compiling only to verbsck.jsalready ships.compile(source) → plan— pure, I/O-free, an inspectable[{verb, payload}]dispatch plan (a caller can audit/authorize it before running).assemble(handle, planOrSource) → { ok, urns, steps }— runs a plan (or a source string) through aConceptKernelhandle via its opendo(verb, payload)form;{$ref}payload values resolve from the sealed ids of earlier steps.Holds the #7 acceptance bar
[{verb, payload}]; no RDF, quad store, or query language emitted. The L1 store-not-graph invariant stays true.compile()resolves/evaluates/traverses nothing.ck.jsunchanged — the module sits strictly aboveConceptKernel, using only its public surface.compile()is pure;tests/smoke-notation.mjsruns over a mock transport viaCK.activate, in the existing smoke-*.mjs style.Scope — slice 1 (instance plane only)
χ/ρ→
create· edges→link· τ→transition· π→verify/provenance. The genome plane (σ/α/γ →propose ▸ vote ▸ apply) is deferred: a genome-plane expression is rejected with a typedNotationErrorrather than pretended, and will degrade honestly togov_plane_unavailablewhen that plane lands (mirroring the existing_govpath).Two deliberate choices, open to your call
assembleruns each step viahandle.do(verb, payload)(the open form) rather than the named conveniences, so the plan stays a uniform[{verb, payload}]and every step is inspectable/governed identically. Equivalent to the named-method calls; happy to switch to per-verb method dispatch if you prefer.target_kernel) are the author's to supply and the server rejects a missing one honestly — the compiler assumes nothing about any type.Tests & wiring
npm testgreen across all suites (smoke-ck / store / client / derived / notation 27/27).package.jsonexports+files+check/testupdated; staged as its own release cut per #7 — not folded into the v1.5.4 scoring byte-set.CHANGELOGunder[Unreleased].Sequencing per #7: orthogonal to the scoring loop; intended to land as its own cut after v1.5.4. No rush on merge — this is here to make the direction concrete and to hand the implementation to cklib to own.